package com.example.snake;

import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

//修改画笔颜色g.setColor(Color.cyan);
//当组件第一次在屏幕上显示时，awt线程会自动调用paint方法来绘制组件
//repaint方法调用时，会自动调用paint方法

/**
 * 1.绘制窗体
 * <p>
 * 2.位置 大小 背景色 标题
 * <p>
 * 3.设置窗口可见
 * <p>
 * 4.关闭窗口的事件监听
 * <p>
 * 5.窗口不能修改大小
 * <p>
 * 6.绘制图形
 * <p>
 * 7.绘制蛇的身体
 * <p>
 * 8.让蛇动起来
 * 1)窗口需要一直重绘
 * 2)修改坐标
 * <p>
 * 9.手动控制方向
 * 1)添加键盘监听事件
 * 2)根据按键修改方向
 * <p>
 * 10.食物
 * 1)随机产生
 * 2)满足格子比例
 * <p>
 * 11.蛇吃食物
 * 1)何时吃到食物
 * 2)产生食物
 * 3)蛇身体加长
 * <p>
 * 12.结束游戏
 * 1)蛇头碰到了身体
 * 2)蛇头碰到了边界
 */

public class SnakeFrame09 extends Frame {
    private static final int WINDOW_X = 200;     //窗口起始点横坐标
    private static final int WINDOW_Y = 100;     //窗口起始点纵坐标
    private static final int WINDOW_W = 640;     //窗口宽度
    private static final int WINDOW_H = 570;     //窗口高度
    //上1，下3，左2，右0
    private static final int UP = 1;
    private static final int DOWN = 3;
    private static final int LEFT = 2;
    private static final int RIGHT = 0;
    //移动速度（屏幕刷新速度）
    private int speed = 150;
    //上1，下3，左2，右0
    private int direction = 0;
    //程序继续则为true
    private boolean isRunning = true;
    //食物结点
    private Node food = new Node(randomNumber(0, WINDOW_W / 20 - 1) * 20, randomNumber(2, WINDOW_H / 20 - 1) * 20);

    //创建一个Node类型的数组，用来存放Node对象 使用匿名对象
    //蛇的主体部分
    private Node[] body = {
            new Node(140, 100),
            new Node(120, 100),
            new Node(100, 100)
    };

    //窗体初始化构造器（构造方法）
    public SnakeFrame09() {
        this.setLocation(WINDOW_X, WINDOW_Y);
        this.setSize(WINDOW_W, WINDOW_H);

        //设置窗口为显示
        this.setVisible(true);
        //设置背景颜色
        this.setBackground(Color.orange);
        this.setTitle("小小贪吃蛇v1.0");

        //禁止修改窗口大小
        this.setResizable(false);

        //注册窗口监听
        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                //退出虚拟机
                System.exit(0);
            }
        });
        //注册键盘监听
        this.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                int dir = -1;
                //上1，下3，左2，右0
                switch (e.getKeyCode()) {
                    case KeyEvent.VK_UP:     //向上走
                        dir = UP;
                        break;
                    case KeyEvent.VK_DOWN:     //向下走
                        dir = DOWN;
                        break;
                    case KeyEvent.VK_LEFT:     //向左走
                        dir = LEFT;
                        break;
                    case KeyEvent.VK_RIGHT:      //向右走
                        dir = RIGHT;
                        break;
                }
                //根据合法性来修改方向 不允许头和身体相撞
                if (direction % 2 != dir % 2) {
                    direction = dir;
                }
            }
        });
        start();   //调用让窗体重绘的方法（刷新）
    }

    /**
     * *绘制图形、线条、文本
     * *画笔
     */
    @Override
    public void paint(Graphics g) {
        //绘制蛇主体部分

        for (int i = 0; i < body.length; i++) {
            body[i].drawNode(g, i);
        }
        //绘制食物部分
        g.setColor(Color.GREEN);
        food.drawNode(g, -1);
        //判断游戏是否结束
        boolean result = isOver();
        if (result) {
            g.setColor(Color.black);
            g.setFont(new Font(null, Font.ITALIC, 50));
            g.drawString("game over!", 200, WINDOW_H / 2);
        }

        //经测试，将此部分代码写到start()方法下，会减少方向键响应延迟，目前不知道原因
        /*if (idEat()) {
                addBody();
            }else {
             move();
         }*/
    }

    /**
     * 让窗体重绘的方法（刷新）
     */
    public void start() {
        while (isRunning) {
            super.repaint();      //手动让窗体重绘
            try {
                Thread.sleep(speed);      //暂停事件
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (idEat()) {
                addBody();     //增加蛇的身体
            } else {
                move();
            }
        }
    }

    /**
     * 修改坐标（移动蛇）
     */
    public void move() {
        //例：向右移动：蛇每次给一个新的头结点（x加20px，y不变），去掉原来的尾结点
        Node[] temp = new Node[body.length];
        temp[0] = getNewHead();     //调用方法获取头结点
        System.arraycopy(body, 0, temp, 1, body.length - 1);
        this.body = temp;
    }

    /**
     * 根据方向来确定一个新的头结点
     *
     * @return
     */
    public Node getNewHead() {
        //上1，下3，左2，右0
        Node newNode = new Node();
        switch (this.direction) {
            case 0:
                newNode.setX(body[0].getX() + 20);
                newNode.setY(body[0].getY());
                break;
            case 1:
                newNode.setX(body[0].getX());
                newNode.setY(body[0].getY() - 20);
                break;
            case 2:
                newNode.setX(body[0].getX() - 20);
                newNode.setY(body[0].getY());
                break;
            case 3:
                newNode.setX(body[0].getX());
                newNode.setY(body[0].getY() + 20);
                break;
        }
        return newNode;
    }

    //随机生成食物
    public void productFood() {
        food.setX(randomNumber(0, WINDOW_W / 20 - 1) * 20);
        food.setY(randomNumber(2, WINDOW_H / 20 - 1) * 20);
    }

    //判断是否吃到了食物
    public boolean idEat() {
        Node newHead = getNewHead();
        if (newHead.equals(food)) {
            return true;
        }
        return false;
    }

    /**
     * 判断游戏是否结束
     *
     * @return
     */
    public boolean isOver() {
        Node head = body[0];
        //因为用的是增强for循环遍历的数组，所以需要重新复制一个去了蛇头的数组temp，用于判断蛇头和身体是否相等
        //用普通for循环不需要这样
        Node[] temp = new Node[body.length - 1];
        System.arraycopy(body, 1, temp, 0, body.length - 1);

        //判断边界
        if (head.getX() < 0 || head.getX() > WINDOW_W - 20 || head.getY() < 30 || head.getY() > WINDOW_H - 20) {
            isRunning = false;
            return true;
        } else {
            for (Node i : temp) {
                if (head.equals(i)) {
                    isRunning = false;
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 吃到食物增加蛇的身体
     * 将食物变为蛇的头部
     * 食物消失，重新生成
     */
    public void addBody() {
        Node[] temp = new Node[body.length + 1];
        temp[0] = food;
        System.arraycopy(body, 0, temp, 1, body.length);
        body = temp;
        food = new Node();
        productFood();
    }

    /**
     * 生成指定区间的随机整数
     *
     * @param min
     * @param max
     * @return
     */
    public static int randomNumber(int min, int max) {
        return (int) (Math.random() * (max - min + 1) + min);
    }

    //主方法
    public static void main(String[] args) {
        new SnakeFrame09();
    }

}
